package com.zhang.learns.netty;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

@Slf4j
public class Worker implements Runnable {
    private String name;
    private Selector selector;
    private volatile boolean inited = false;

    public Worker(String name) {
        this.name = name;
    }

    public void registry(SocketChannel socketChannel) {
        try {
            if (!inited) {
                selector = Selector.open();

                Thread thread = new Thread(this, name);
                thread.start();

                inited = true;
            }
            socketChannel.configureBlocking(false);
            selector.wakeup();
            socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(16));
        } catch (ClosedChannelException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                selector.select();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            log.info("这是客户端:{}",name);

            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            if (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                if (selectionKey.isReadable()) {
                    try {
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
                        int readLen = socketChannel.read(byteBuffer);
                        if (readLen > 0) {
                            splite(byteBuffer);
                            //扩容
                            if (byteBuffer.position() == byteBuffer.limit()) {
                                ByteBuffer newByteffer = ByteBuffer.allocate(byteBuffer.capacity() * 2);
                                byteBuffer.flip();
                                newByteffer.put(byteBuffer);
                                selectionKey.attach(newByteffer);
                            }
                        } else if (readLen == -1) {
                            selectionKey.cancel();
                        }
                    } catch (IOException e) {
                        selectionKey.cancel();
                        throw new RuntimeException(e);
                    }

                }
            }
        }
    }

    private static void splite(ByteBuffer source) {
        // byteBuffer切换为读模式
        source.flip();
        for (int i = 0; i < source.limit(); i++) {
            if (source.get(i) == '\n') {
                int len = i + 1 - source.position();
                ByteBuffer target = ByteBuffer.allocate(len);
                for (int j = 0; j < len; j++) {
                    target.put(source.get());
                }
                target.flip();
                System.out.println(StandardCharsets.UTF_8.decode(target).toString());
            }
        }
        // compact方法是整理压缩buffer,之后可以在这个buffer上继续写操作追加到后面
        source.compact();
    }
}
